package leetcode;

import java.util.*;

/**
 * 51. N 皇后
 * n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上，并且使皇后彼此之间不能相互攻击。
 * <p>
 * 给你一个整数 n ，返回所有不同的 n 皇后问题 的解决方案。
 * <p>
 * 每一种解法包含一个不同的 n 皇后问题 的棋子放置方案，该方案中 'Q' 和 '.' 分别代表了皇后和空位。
 * <p>
 * <p>
 * <p>
 * 示例 1：
 * <p>
 * <p>
 * 输入：n = 4
 * 输出：[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
 * 解释：如上图所示，4 皇后问题存在两个不同的解法。
 * 示例 2：
 * <p>
 * 输入：n = 1
 * 输出：[["Q"]]
 * <p>
 * <p>
 * <p>
 *
 * 皇后的走法是：可以横直斜走，格数不限。因此要求皇后彼此之间不能相互攻击，等价于要求任何两个皇后都不能在同一行、同一列以及同一条斜线上。
 */
public class SolveNQueens {

    List<List<String>> solutions = new ArrayList<List<String>>();


    public List<List<String>> solveNQueens(int n) {
        // index为行，value为列
        int[] queens = new int[n];
        Arrays.fill(queens, -1);
        // 列
        backtrack(queens, n, 0, new HashSet<>(), new HashSet<>(), new HashSet<>());

        return solutions;
    }

    public void backtrack(int[] queens, int n, int row,
                          Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
        if (row == n) {
            List<String> board = generateBoard(queens, n);
            solutions.add(board);
        } else {
            // 不管第几行，都是从第一列开始遍历
            for (int col = 0; col < n; col++) {
                if (columns.contains(col)) {
                    continue;
                }
                // 左上 到 右下 的斜线。左上的已经遍历过了
                // diagonal1指的是从左上到右下的某条确定的斜线
                int diagonal1 = row - col;
                if (diagonals1.contains(diagonal1)) {
                    continue;
                }
                // 右上 到 左下 的斜线。右上的已经遍历过了
                int diagonal2 = row + col;
                if (diagonals2.contains(diagonal2)) {
                    continue;
                }
                // row 行 第i列 可以放上
                queens[row] = col;

                columns.add(col);
                diagonals1.add(diagonal1);
                diagonals2.add(diagonal2);

                // 到这可以停止，因为这一行刚才已经加了，继续遍历下一行了
                backtrack(queens, n, row + 1, columns, diagonals1, diagonals2);

                // row行i列满足，但之后可能不满足，即row+1行不能满足。此时需要清掉i这一列，尝试row行的下一列
                queens[row] = -1;
                columns.remove(col);
                diagonals1.remove(diagonal1);
                diagonals2.remove(diagonal2);
            }
        }
    }

    public List<String> generateBoard(int[] queens, int n) {
        List<String> board = new ArrayList<String>();
        for (int i = 0; i < n; i++) {
            char[] row = new char[n];
            Arrays.fill(row, '.');
            // i 这一行的 第 queens[i] 列是'Q'
            row[queens[i]] = 'Q';
            board.add(new String(row));
        }
        return board;
    }

}
